Skip to content

Add native Hill dose-response fitting - #1097

Merged
Zethson merged 9 commits into
scverse:mainfrom
daveringelberg:hill-dose-response
Sep 23, 2026
Merged

Zethson merged 9 commits into
scverse:mainfrom
daveringelberg:hill-dose-response

Conversation

@daveringelberg

@daveringelberg daveringelberg commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Adds fit_dose_response and plot_dose_response, which fit and plot a four-parameter Hill curve per perturbation for the distance or any gene, storing EC50 and the curve parameters in .obs.
dose_response now returns a perturbation-by-dose AnnData with mean expression in X instead of a DataFrame, which is a breaking change to the 1.2.0 API.
Companion tutorial: scverse/pertpy-tutorials#77.

@codecov-commenter

codecov-commenter commented Sep 9, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.63014% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 80.27%. Comparing base (d8820e3) to head (4e088d2).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...y/tools/_perturbation_space/_perturbation_space.py 98.63% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1097      +/-   ##
==========================================
+ Coverage   80.12%   80.27%   +0.15%     
==========================================
  Files          55       55              
  Lines        7571     7635      +64     
==========================================
+ Hits         6066     6129      +63     
- Misses       1505     1506       +1     
Files with missing lines Coverage Δ
...y/tools/_perturbation_space/_perturbation_space.py 90.41% <98.63%> (+2.25%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Zethson

Zethson commented Sep 9, 2026

Copy link
Copy Markdown
Member

Great! Let me know when you want me to have a look, please.

@daveringelberg

Copy link
Copy Markdown
Contributor Author

Thanks! It's ready for review whenever you have time.

@Zethson Zethson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I think we're getting there.

Comment thread docs/api/tools_index.md
Comment thread docs/api/tools_index.md Outdated
Comment thread docs/api/tools_index.md Outdated
Comment thread docs/api/tools_index.md Outdated
Comment thread docs/api/tools_index.md Outdated
Comment thread src/pertpy/tools/_perturbation_space/_perturbation_space.py Outdated
Comment thread src/pertpy/tools/_perturbation_space/_perturbation_space.py Outdated
Comment thread src/pertpy/tools/_perturbation_space/_perturbation_space.py
Comment thread src/pertpy/tools/_perturbation_space/_perturbation_space.py Outdated
Comment thread tests/tools/_perturbation_space/test_perturbation_space_extras.py Outdated
@daveringelberg

Copy link
Copy Markdown
Contributor Author

Thanks for the review! I've updated the fitter to store results in AnnData, shortened the documentation and examples, added the API links, and made the integration test use the existing RNG fixture again. I've also added a short assay-response example to the tutorial.

@Zethson

Zethson commented Sep 21, 2026

Copy link
Copy Markdown
Member

I'll finalize it at the end of this week

fit_dose_response only used the AnnData as a sink for .uns, so callers with
assay tables had to build an empty AnnData. It now takes the tidy table and
returns the fits, matching dose_response and evaluate_combinations.

A perturbation with too few doses, a constant response or a non-converging
fit now warns and gets a NaN row instead of aborting the whole screen.
Drop response_type, which only renamed ec50 to ic50, and document that e0
is extrapolated when the reference group is absent from the input.
dose_response now returns a perturbation-by-dose AnnData (group means in X,
distance and group-constant obs columns in .obs) instead of a DataFrame,
matching PseudobulkSpace.compute. This is a breaking change to the 1.2.0 API.

fit_dose_response reads perturbation, dose and response from .obs and writes
the fitted response and the per-perturbation Hill parameters as hill_*
columns to .obs, so results stay in AnnData without .uns. Assay data works
as an AnnData whose .obs holds one row per well.
dose_response now stores the mean expression of adata.X (or layer_key) in
X so var matches the input genes; embedding_key only selects the distance
space. fit_dose_response takes a response that is an .obs column or a gene
and prefixes its .obs columns with it, so several responses can be fit
side by side. plot_dose_response replaces hand-written plotting code.
@Zethson
Zethson merged commit 568784f into scverse:main Sep 23, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants